/* Measure.h
 *
 * Created on: Mar 12, 2012
 * Author: snirgaz
 */

#ifndef MEASURE_H_
#define MEASURE_H_
#include "../HelpLibs/Observable.h"
#include "Lattice.h"
#include "../HelpLibs/QmcParameters.h"
#include "../HelpLibs/def.h"
#include "../HelpLibs/fftwPlane.h"
#include "boost/chrono.hpp"
#include "boost/rational.hpp"
#include <map>
#include <functional>
using namespace std;

typedef boost::chrono::system_clock::time_point TimePoint;
typedef boost::chrono::duration<double, boost::ratio<3600> > Hour;
typedef boost::rational<int> rationalNumber;
class ProgressBar {
	int totalNumberOfMeasure_;
	ofstream *outFile_;
	double simTime_;
	TimePoint startTime_;
	double pc_;
	double deltaDump_;
public:
	void init(int totalNumberOfMeasure, int minNumOfMeasure, double simTime,
			double deltaDump, ofstream *outFile);
	void update(int numOfMeasure, bool &endTime, bool &dumpFlag);
	void updateMinNumOfMeasure(int minNumOfMeasure);
};
enum MeasureType {
	OPEN, CLOSED
};

class Measure {
protected:
	MeasureType measureType_;
	Lattice *lat_;
public:
	Measure();
	virtual void DoMeasurement()=0;
	virtual bool checkDone() const =0;
	virtual void dumpToFile(hdf5RW &hdf5File) const=0;
	virtual int getNumOfMeasure() const=0;
	virtual void readFromFile(hdf5RW &hdf5File)=0;
	MeasureType getMeasureType() const;
	virtual ~Measure();
};

class ZGRatioMeasure: public Measure {
	Observable<ScalarObs<double> > ZGRatio_;
	BoltzWeightType *boltzRatio_;
public:
	ZGRatioMeasure(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio);
	~ZGRatioMeasure();
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
class ZZPRatioMeasure: public Measure {
	Observable<ScalarObs<double> > ZZPRatio_;
	BoltzWeightType *boltzRatio_;
public:
	ZZPRatioMeasure(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio);
	~ZZPRatioMeasure();
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};

class NormRatioMeasure: public Measure {
	Observable<ScalarObs<double> > normRatio_;
public:
	NormRatioMeasure(Lattice *lat, QmcParameters &qmcParameters);
	~NormRatioMeasure();
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};

class GreenMeasure: public Measure {
	typedef ArrayObservable<Observable<ScalarObs<double> >, DIM> obs;
	typedef obs::IterIndex obsIterIndex;
	typedef Observable<ScalarObs<double> >::InitData InitData;
	obs green_;
public:
	GreenMeasure(Lattice *lat, QmcParameters &qmcParameters);
	~GreenMeasure();
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};

class WindingEnergyMeasure: public Measure {
	typedef Observable<ScalarObs<double> > obs;
	obs wind_, energy_, wecov_, energyVar_;
	BoltzWeightType *boltzRatio_;
	double latSize_;
public:
	WindingEnergyMeasure(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio);
	WindingEnergyMeasure();
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
	int getLastMeasure();
};

struct KPointData {
	int numOfPoints;
	double obs;
};
rationalNumber calcNorm(IndexType const & kIndex, int L);

class AmpCalcFFT {
	typedef ArrayMulti<double, DIM> ArrayData;
	typedef VectorObs<double>::mean_type VectorData;
	typedef ArrayData::iterator ArrayDataIter;
	typedef ArrayMulti<complex<double>, DIM> ArrayDataComplex;
	typedef ArrayDataComplex::iterator ArrayDataComplexIter;
	BoltzWeightType *boltzRatio_;
	ArrayData addOne_;
	ArrayDataComplex fftDensity_;
	ArrayData powerSpect_;
	fftwPlan fPlane_;
	Lattice *lat_;
	IndexType latSize_;
	double amp_, ampSqr_;
	double numOfSite_, numOfSpatialSites_;
public:
	void init(Lattice *lat, QmcParameters & qmcParameters,
			BoltzWeightType *boltzRatio);
	void calcAmpFFT();
	void calcAmp();
	ArrayData& getPowerSpect();
	double getAmplitudeVar() const;
	double getAmplitudeVarSqr() const;

};
class AmplitudeMeasureCorr: public Measure {
//  typedef ArrayObservable<Observable<ScalarObs<double> >, 1> obsArray;
	typedef Observable<VectorObs<double>> obsArray;
	obsArray amplitudeCorr_;
	int corrSize_;
	AmpCalcFFT &ampCalcFFT_;
	BoltzWeightType *boltzRatio_;
public:
	AmplitudeMeasureCorr(QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio, AmpCalcFFT &ampCalcFFT);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
class AmplitudeMeasureVar: public Measure {
	typedef ScalarBinObs<double> obs;
	obs amplitude_, amplitudesqr_;
	AmpCalcFFT &ampCalcFFT_;
	BoltzWeightType *boltzRatio_;
public:
	AmplitudeMeasureVar(QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio, AmpCalcFFT &ampCalcFFT);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
class AmplitudeMeasure: public Measure {
	typedef boost::ptr_vector<Measure> MeasureVector;
	MeasureVector measurements_;
	BoltzWeightType *boltzRatio_;
	AmpCalcFFT ampCalcFFT_;
	bool fft_;
public:
	AmplitudeMeasure(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};

class Measurements {
	typedef boost::ptr_vector<Measure> MeasureVector;
	typedef MeasureVector::iterator MeasureVectorIter;
	int numOfBins_;
private:
	MeasureVector measures_;
	Lattice *lat_;
	ProgressBar progBar_;
	int rateOpen_, rateClosed_;
	bool allDone_;
	ofstream *outFile_;
public:
	Measurements();
	~Measurements();
	void init(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *weight, ofstream *outFile);
	void DoMeasurement(bool &endTime, bool &dumpTime);
	bool checkNotDone() const;
	void dump(string outPath) const;
	void read(string outPath);
	void updateMinNumOfMeasure(int totalNumberOfMeasure);
	void updateClosedRate(int const closedRate);
	int getClosedRate() const;

};
#endif /* MEASURE_H_ */
